/*
 * Created on 09-sep-2004
 */
package guru.mercurio.core;

import guru.mercurio.common.AbsoluteApplicationAddress;
import guru.mercurio.common.RelativeApplicationAddress;
import guru.mercurio.data.ChannelDataManager;
import guru.mercurio.data.PassportDataManager;
import guru.merlin.bus.EnvelopeBus;
import guru.merlin.core.Address;
//import guru.merlin.core.CantHandException;
import guru.merlin.core.EPException;
import guru.merlin.core.Envelope;
import guru.merlin.core.EnvelopeEPException;
import guru.merlin.core.InfrastructureEPException;
import guru.merlin.core.Router;
import guru.util.Commander;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;

import org.apache.log4j.Logger;

/**
 * Pasos: 1. Gateway: transformacin del protocolo. 2. Router: direccionamiento a travs del nodo
 * correspondiente.
 *
 * @author cobach
 */

public class MercurioRouter extends Router{
    static Logger logger = Logger.getLogger(MercurioRouter.class);
    
    private ChannelDataManager cdm;
    
    private PassportDataManager pdm;
    
    public MercurioRouter(EnvelopeBus bus, Commander commander, String xmlRoutingTableFilePath) throws Exception {
        super(bus, commander, xmlRoutingTableFilePath);
        
        String driver = commander.getValue("db.driver");
        String dbmsURL = commander.getValue("db.dbmsURL");
        String username = commander.getValue("db.username");
        String password = commander.getValue("db.password");
        
        cdm = new ChannelDataManager(driver, dbmsURL, username, password);
        pdm = new PassportDataManager(cdm.dm);
    }
    
    /**
     * Routing logic
     */
    public Envelope preRouteEnvelope(Envelope envelope) throws EPException {
        String msg = "";
        if(logger.isDebugEnabled()) {
            msg = "Message received by pre router: " + envelope.toString();
            logger.debug(msg);
        }
        if (!envelope.wellFormed()) { 
        	throw new EnvelopeEPException("Envelope not well formed"); 
        }
        
        if (envelope.getContent() instanceof String) {
            String c = (String) envelope.getContent();
            if (c.length() > 129) {
                envelope.setContent(c.substring(0, 129));
            }
        }
        
        return (envelope);
    }
    
    /*
     * (non-Javadoc)
     *
     * @see guru.merlin.core.Router#getVersion()
     */
    public String getVersion() {
        return "Mercurio Router version 2.2.4";
    }
    
    private String getPassportByChannel(Envelope envelope) throws Exception {
        AbsoluteApplicationAddress app = null;
        AbsoluteApplicationAddress[] apps = null;
        String text,word,channelId = "";
        String rtn = "";
        int pos;
        try {
            channelId = envelope.getDestination().getPort();
            if ((pos = (text = envelope.getContent().toString()).indexOf(" ")) != -1) {
                word = text.substring(0, pos);
            } else {
                word = text;
            }
            apps = cdm.getApplicationBySubChannel(channelId, word);
            if (apps!=null && apps.length == 1) {
                app = apps[0];
            } else if (apps!=null && apps.length == 0) {
                apps = cdm.getApplicationByChannel(channelId);
                if (apps!=null && apps.length == 1) {
                    app = apps[0];
                }
            } else {
                throw new Exception();
            }
        } catch (Exception e) {
            try {
                apps = cdm.getApplicationByChannel(channelId);
                if (apps!=null && apps.length == 1) {
                    app = apps[0];
                }
            } catch (Exception e1) {
                throw new Exception();
            }
        } finally {
            if (app != null && app.getPassport()!=null) {
                rtn = app.getPassport();
            } else {
                this.handleError(envelope, new Exception("Atention: Was not a passport to which to assign mdr.  Passport doesn't exist."), "Handling Error");
            }
        }
        return rtn;
    }
    
    public Object preProcesingAction(Envelope envelope) throws Exception {
        logger.info(envelope.toStringNoContent());
        String passport = "";
        Address from = envelope.getOrigin();
        Address to = envelope.getDestination();
        
        logger.debug("Procesing envelope from: " + from.getClass().getName() + " to: " + to.getClass().getName());
        
        
        //Buscando el pasaporte que pertenece a esta transaccin
        if (from instanceof RelativeApplicationAddress) {
            passport = ((RelativeApplicationAddress) from).getVariable();
        } else if (to instanceof RelativeApplicationAddress) {
            passport = getPassportByChannel(envelope);
            if (passport==null || passport.equals("")){
                //return null;
            	throw new Exception("No encontré pasaporte asociado");
            }
        } else {
            throw new Exception("Atention: Was not a passport to which to assign mdr ");
        }
        
        Long mrd_id = pdm.createMDR(passport, from.toString(), to.toString(), envelope.getContent().toString().length(), envelope.getComment(), Integer.parseInt(envelope.getId()), -1);
        return new PreProcessingReference(envelope, mrd_id, passport);
    }
    
    public void preProcesingActionCompensator(Object actionReference) throws Exception {
        PreProcessingReference ppr = null;
        try {
            ppr = (PreProcessingReference) actionReference;
        } catch (Exception e) {
            throw e;
        }
        
        Envelope envelope = ppr.getEnvelope();
        
        String passport = "";
        Address from = envelope.getOrigin();
        Address to = envelope.getDestination();
        //AbsoluteApplicationAddress aaa;
        
        if (from instanceof RelativeApplicationAddress) {
            passport = ((RelativeApplicationAddress) from).getVariable();
        } else if (to instanceof RelativeApplicationAddress) {
            passport = ppr.getPassport();
        } else {
            throw new Exception("ATENTION: The passport of mdr was not able to eliminate ");
        }
        pdm.deleteMDR(passport, to.toString(), Integer.parseInt(envelope.getId()), ppr.getMdr_id());
    }
    
    /**
     * Hace persistente el procesamientos de un objeto Envelope. Actualiza el campo "commit" de la
     * base de datos MDR
     *
     * @param actionReference
     *            Objeto referencia del preprocesamiento realizado.
     * @throws CantHandException
     */
    public void commitPreProcesingAction(Object actionReference) throws EPException {
        PreProcessingReference ppr = (PreProcessingReference) actionReference;
        try {
            pdm.setCommitMDR(ppr.getMdr_id());
        } catch (Exception e) {
            throw new InfrastructureEPException(e.toString());
        }
    }
    
    public String getTime() {
        Calendar calendar = new GregorianCalendar();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        return formatter.format(calendar.getTime());
    }
    
    /**
     * @author sventura
     *
     * Los objetos de esta clase son pasados como referencia a los mtodo
     * preProcesingActionCompensator y handenvelope del procesin action, en el EPContainer. Ventana -
     * Preferencias - Java - Estilo de cdigo - Plantillas de cdigo
     */
    class PreProcessingReference{
        Envelope envelope;
        Long mdr_id;
        String passport;
        
        /**
         * @param envelope
         * @param mdr_id
         */
        public PreProcessingReference(Envelope envelope, Long mdr_id, String passport) {
            super();
            this.envelope = envelope;
            this.mdr_id = mdr_id;
        }
        
        /**
         * @return Devuelve envelope.
         */
        public Envelope getEnvelope() {
            return envelope;
        }
        
        /**
         * @param envelope
         *            El envelope a establecer.
         */
        public void setEnvelope(Envelope envelope) {
            this.envelope = envelope;
        }
        
        /**
         * @return Devuelve mdr_id.
         */
        public Long getMdr_id() {
            return mdr_id;
        }
        
        /**
         * @param mdr_id
         *            El mdr_id a establecer.
         */
        public void setMdr_id(Long mdr_id) {
            this.mdr_id = mdr_id;
        }
        
        /**
         * @return Devuelve passport.
         */
        public String getPassport() {
            return passport;
        }
        
        /**
         * @param passport
         *            El passport a establecer.
         */
        public void setPassport(String passport) {
            this.passport = passport;
        }
    }
    
}